1.1 使用 Flutter webview_flutter 库引入 WebView 能力
WebView 是移动开发中最常使用的控件之一,也是最为复杂的控件之一。在 Flutter 开发中,也需要 WebView 能力。因此,Google 官方团队开发了 webview_flutter 库,它利用 PlatformView 机制,对 Android、iOS 的 WebView 进行封装,封装为一个 Flutter 组件。开发者在使用时,如同使用纯 Flutter 组件一般,而在底层,是嵌入了原生 WebView 视图能力。
Flutter PlatformView 在桌面端的支持并不完善,因此长期以来 webview_flutter 局限于 Android、iOS 平台
从 Flutter 3.24 开始,macOS 下 PlatformView 趋于完善,webview_flutter 已可运行于 macOS 平台。具体参见《Flutter macOS WebView(3.24 后)》
WebPage
引入 webview_flutter 库后,WebView 的页面实现如下。采用 Flutter 下常用的 Controller 模式——WebViewWidget 组件负责展示,WebViewController 负责控制。我们使用 StatefulWidget。
class WebViewExample extends StatefulWidget {
const WebViewExample({super.key});
@override
State<WebViewExample> createState() => _WebViewExampleState();
}
class _WebViewExampleState extends State<WebViewExample> {
// Webview 控制器,很多功能都通过它实现
late final WebViewController _controller;
// 创建 WebView 的初始化参数,不同平台不一样
late final PlatformWebViewControllerCreationParams params;
@override
void initState() {
super.initState();
// WebViewPlatform.instance 插件初始化时会自动赋值,不同平台类型不同
if (WebViewPlatform.instance is WebKitWebViewPlatform) {
params = WebKitWebViewControllerCreationParams(
allowsInlineMediaPlayback: true,
mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
);
} else {
params = const PlatformWebViewControllerCreationParams();
}
// 工厂方法创建 WebViewController
final WebViewController controller =
WebViewController.fromPlatformCreationParams(params);
// controller 通过链式方法进行配置
controller
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
// 略....
..addJavaScriptChannel(
'Toaster',
onMessageReceived: (JavaScriptMessage message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message.message)),
);
},
)
// 发起请求
..loadRequest(Uri.parse('https://flutter.dev'));
// 保存 controller 实例,后续需传入 WebViewWidget
_controller = controller;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.green,
appBar: AppBar(/* 略... */)
// WebView 视图组件
body: WebViewWidget(controller: _controller),
);
}
// ...
}
其中,包含两个核心元素:
-
WebViewController
负责处理与每个平台提供的底层网页视图相关的所有功能(例如,加载网址、设置底层平台视图的背景颜色或清除缓存)。 -
WebViewWidget
接收一个WebViewController
并处理所有与 Flutter 小部件相关的功能(例如,布局方向、手势识别器)。
WebViewWidget
这是 webview_flutter 封装好后的视图组件。WebViewWidget 是一个 StatelessWidget,主要实现如下:
class WebViewWidget extends StatelessWidget {
WebViewWidget({
Key? key,
required WebViewController controller,
TextDirection layoutDirection = TextDirection.ltr,
Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers =
const <Factory<OneSequenceGestureRecognizer>>{},
}) : this.fromPlatformCreationParams(
key: key,
params: PlatformWebViewWidgetCreationParams(
// 实际需要的是 controller 中的 platform
controller: controller.platform,
layoutDirection: layoutDirection,
gestureRecognizers: gestureRecognizers,
),
);
// 在 PlatformWebViewWidget 构造方法中,又访问到 WebViewPlatform.instance
// WebViewPlatform.instance 在不同平台下,有不同的实现
WebViewWidget.fromPlatformCreationParams({
Key? key,
required PlatformWebViewWidgetCreationParams params,
}) : this.fromPlatform(key: key, platform: PlatformWebViewWidget(params));
/// 最终的实例构造方法,需要一个 key,一个 PlatformWebViewWidget
WebViewWidget.fromPlatform({super.key, required this.platform});
/// Implementation of [PlatformWebViewWidget] for the current platform.
final PlatformWebViewWidget platform;
// ...
@override
Widget build(BuildContext context) {
// 可以看到,PlatformWebViewWidget 自身并非组件,而是一个工厂
return platform.build(context);
}
}
综合来看,WebViewWidget 根据传入的平台,创建 PlatformWebViewWidget 实例,PlatformWebViewWidget 内部会根据 WebViewPlatform.instance 在不同平台下,有不同的实现。
PlatformWebViewWidget 自身是一个工厂,通过 build 方法创建出不同平台下的 Flutter Webview Widget。
PlatformWebViewWidget 的实现类们
PlatformWebViewWidget 是个抽象类,通过上一节分析可知,不同平台下,有 PlatformWebViewWidget 的不同实现类,具体包括:
- AndroidWebViewWidget:Android 平台
- WebWebViewWidget:Web 平台
- WebKitWebViewWidget:iOS 平台
webview_flutter 一共就支持这三个平台。
PlatformWebViewWidget.build
下面我们来看他们的 build 方法:
AndroidWebViewWidget.build:
在《Flutter 内嵌原生视图 Android 端接入实现》中提到,Flutter 在 Android 下 PlatformView 有 3 中显示模式,其中 Hybrid composition 和 Texture Layer Hybrid Composition 都需要使用 PlatformViewLink 组件进行封装。
下面的 PlatformViewLink 具体实现,可以与《Flutter 内嵌原生视图 Android 端接入实现》中官方文档中给出的默认实现相对比,来帮助理解。
@override
Widget build(BuildContext context) {
// ...
return PlatformViewLink(
// Setting a default key using `params` ensures the `PlatformViewLink`
// recreates the PlatformView when changes are made.
key: _androidParams.key ??
ValueKey<AndroidWebViewWidgetCreationParams>(
params as AndroidWebViewWidgetCreationParams),
viewType: 'plugins.flutter.io/webview',
surfaceFactory: (
BuildContext context,
PlatformViewController controller,
) {
return AndroidViewSurface(
controller: controller as AndroidViewController,
gestureRecognizers: _androidParams.gestureRecognizers,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
},
onCreatePlatformView: (PlatformViewCreationParams params) {
return _initAndroidView(
params,
displayWithHybridComposition:
_androidParams.displayWithHybridComposition,
platformViewsServiceProxy: _androidParams.platformViewsServiceProxy,
view:
(_androidParams.controller as AndroidWebViewController)._webView,
instanceManager: _androidParams.instanceManager,
layoutDirection: _androidParams.layoutDirection,
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
..create();
},
);
其中引人注意的是这一段:
return _initAndroidView(
params,
displayWithHybridComposition:
_androidParams.displayWithHybridComposition,
platformViewsServiceProxy: _androidParams.platformViewsServiceProxy,
view:
(_androidParams.controller as AndroidWebViewController)._webView,
instanceManager: _androidParams.instanceManager,
layoutDirection: _androidParams.layoutDirection,
)
这段代码关系到 webview_flutter 以何种模式进行渲染,尤其是 displayWithHybridComposition
这个设置选项,表示开发者是否强制开启 Hybrid composition 模式(具体参见《Flutter webview_flutter Android 端实现原理》)。
下面看 _initAndroidView
的实现:
AndroidViewController _initAndroidView(
PlatformViewCreationParams params, {
//...
}) {
final int? instanceId = instanceManager.getIdentifier(view);
// 默认为 false
if (displayWithHybridComposition) {
// Flutter 3.0 后,该方法表示 Flutter Hybrid composition|Hybrid composition
return platformViewsServiceProxy.initExpensiveAndroidView(
id: params.id,
viewType: 'plugins.flutter.io/webview',
layoutDirection: layoutDirection,
creationParams: instanceId,
creationParamsCodec: const StandardMessageCodec(),
);
} else {
// Flutter 3.0 后,该方法表示 Texture Layer Hybrid Composition
return platformViewsServiceProxy.initSurfaceAndroidView(
id: params.id,
viewType: 'plugins.flutter.io/webview',
layoutDirection: layoutDirection,
creationParams: instanceId,
creationParamsCodec: const StandardMessageCodec(),
);
}
}
这两个 init 方法都是代理方法,分别看一下他们的具体实现:
platformViewsServiceProxy.initExpensiveAndroidView
:
/// Proxy method for [PlatformViewsService.initExpensiveAndroidView].
ExpensiveAndroidViewController initExpensiveAndroidView({
required int id,
required String viewType,
required TextDirection layoutDirection,
dynamic creationParams,
MessageCodec<dynamic>? creationParamsCodec,
VoidCallback? onFocus,
}) {
// 调用 Flutter 框架 API
return PlatformViewsService.initExpensiveAndroidView(
id: id,
viewType: viewType,
layoutDirection: layoutDirection,
creationParams: creationParams,
creationParamsCodec: creationParamsCodec,
onFocus: onFocus,
);
}
platformViewsServiceProxy.initSurfaceAndroidView
:
SurfaceAndroidViewController initSurfaceAndroidView({
required int id,
required String viewType,
required TextDirection layoutDirection,
dynamic creationParams,
MessageCodec<dynamic>? creationParamsCodec,
VoidCallback? onFocus,
}) {
// 调用 Flutter 框架 API
return PlatformViewsService.initSurfaceAndroidView(
id: id,
viewType: viewType,
layoutDirection: layoutDirection,
creationParams: creationParams,
creationParamsCodec: creationParamsCodec,
onFocus: onFocus,
);
}
legacy WebView
在 webview_flutter 中,还包含一套已经废弃的旧实现 WebView
.
目前,在新实现中,WebView
的功能由 WebViewController
和 WebViewWidget
所继任。
因此,我们需要注意,不要使用 WebView
了,而是按照本文开头方式,或者文档进行使用。
网络资源
- 在 Flutter 中使用 webview_flutter 4.0 | js 交互 - 掘金
- webview_flutter | Flutter Package
- Flutter Web Browser Tutorial: A Comprehensive Guide for Beginners
本文作者:Maeiee
本文链接:1.1 使用 Flutter webview_flutter 库引入 WebView 能力
版权声明:如无特别声明,本文即为原创文章,版权归 Maeiee 所有,未经允许不得转载!
喜欢我文章的朋友请随缘打赏,鼓励我创作更多更好的作品!